#pragma once

#include <vector>
#include <mutex>

#include "zen_app.h"
#include "zen_wrap.h"
#include "zen_class.h"
#include "zen_game_matrix.h"
#include "zen_game_touch.h"
#include "zen_game_graphics.h"

/*!
@header zen_game_node.h
@abstract basic class node and the part-class.
@author mehertj
@version v1.0
*/
namespace Zen
{
	namespace Game
	{

		class Action;

		class NodeDelegate
		{
		public:
			virtual ~NodeDelegate() = default;
			virtual void OnLoad() {}
			virtual void OnUnload() {}
			virtual void Flush() {}
			virtual void OnReady(std::shared_ptr<RenderStack> stack, float interval) {}
			virtual void OnDraw() {}
		};

		class Node : public Object, public NodeDelegate
		{
		public:
			using NodePtr = std::shared_ptr<Node>;

		public:
			/*
			 * Action
			 */
			void StopAttachedActions();
			void RemoveAttachedTouches();

		public:
			/*
			 * Nodes
			 */
			template <typename NodeType>
			auto CreateInnerNode() -> std::shared_ptr<NodeType>;

			void AddNode(std::shared_ptr<Node>);

			void ReleaseInnerNodes();

			void SortInnerNodes();

			auto GetInnerNodes() -> std::vector<NodePtr>;

			// remove this and all its children.
			void ReleaseSelf();

		public:
			/*
			 * Order
			 */
			void SetSiblingOrder(int index); // the order in brothers. the less, the erlier drawing.
			int GetSiblingOrder() const;
			std::shared_ptr<Node> GetUpperNode();

		protected:
			std::weak_ptr<Node> mUpperNodeRef;
			std::vector<NodePtr> mInnerNodes;
			int mSiblingOrder{};
			bool mIsInnerOrderDirty = false;

		public:
			/*
			 * Visible
			 * = is OnDraw called.
			 */
			void SetVisible(bool v);
			bool IsVisible() const;

			/*
			 * = is Event(action, touch and soon) called.
			 */
			void SetEnabled(bool v);
			bool IsEnabled() const;

		protected:
			bool mIsVisible = true;
			bool mIsEnabled = true;
		};

		template <typename NodeType>
		inline auto Node::CreateInnerNode() -> std::shared_ptr<NodeType>
		{
			auto node = std::make_shared<NodeType>();
			this->AddNode(node);
			return node;
		}

		class NodeWithSampler
		{
		public:
			virtual ~NodeWithSampler() = default;
			void SetSampler(Zen::Graphics::SamplerMode mode);
			Zen::Graphics::SamplerMode const& GetSampler() const;

		protected:
			bool mIsSamplerDirty{ true };
			Zen::Graphics::SamplerMode mSampler{};
		};

		/// node attribute: color
		/// that multiply to final pixel.
		class NodeWithColor
		{
		public:
			virtual ~NodeWithColor() = default;

		public:
			void SetColor(Color4f const& color);
			Color4f const& GetColor();

		protected:
			bool mIsColorDirty{};
			Color4f mColor = EColor::White;
		};

		template <typename GraphicsProgram_>
		class NodeWithGraphicsProgram
		{
		public:
			using GraphicsProgram = GraphicsProgram_;

			virtual ~NodeWithGraphicsProgram() = default;

			void SetProgram(std::shared_ptr<GraphicsProgram_> program)
			{
				mProgram = program;
			}
			std::shared_ptr<GraphicsProgram_> GetProgram()
			{
				return mProgram;
			}
			void SetDrawMode(Zen::Graphics::EDrawMode mode)
			{
				mDrawMode = mode;
			}
			Zen::Graphics::EDrawMode GetDrawMode()
			{
				return mDrawMode;
			}
			void SetBlend(Zen::Graphics::EBlend blend)
			{
				mBlend = blend;
			}
			Zen::Graphics::EFace GetBlend()
			{
				return mBlend;
			}

		protected:
			Zen::Graphics::EDrawMode mDrawMode{ Zen::Graphics::EDrawMode::TriangleStrip };
			Zen::Graphics::EBlend mBlend{ Zen::Graphics::EBlend::Normal };
			Zen::Graphics::EFace mCulled{ Zen::Graphics::EFace::None };
			std::shared_ptr<GraphicsProgram_> mProgram;
		};

	} // namespace Game
} // namespace Zen
